iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 21
1
自我挑戰組

非本科之30天Ruby / Rails學習筆記系列 第 21

Day21: 簡易Rails實作(中)

  • 分享至 

  • xImage
  •  

上次透過簡單首頁、分頁製作了第一個rails專案,了解到其中MVC的運作原理及routes與controller之間的關聯性,這次我們將延續上次的內容添加一些功能,並透過CRUD 中(Create、 Read、 Update、 Delete)C跟R的流程來建立專案,U和D的部分就暫時有機會的時候再說明了,現在就來做做看吧!

新增候選人至表單

在new.html.erb檔案中,我們需要一個表單讓使用者輸入,輸入後把資料丟到我們想要去的地方,先來建立表單吧!

<h1>新增候選人</h1>

<form action="/candidates" method="post">
  <input type="text" name="username">
  <input type="submit">
</form>

<%= link_to '回首頁', candidates_path, class: 'btn btn-primary btn-lg'%>

其中我們可以看到第3行的action指的是我們要將資料丟去哪裡,method則是丟出去的方法。

(在終端機執行rails routes)

使用者填完表單並按送出後,會將資料丟去

candidates POST / candidates(.:format) candidates#create:
candidates(.:format) 這個地方丟資料去candidates#create的時候,需要用 post 方法。
此例我們要丟去的位置是create,且要用post的方式丟出,所以可以看到第3行 action="/candidate" method="post" 的部分才要用post而不是get

此時的畫面長這樣:

這時候如果我們隨便輸入東西並按下提交,會出現下圖:

還記得上次的步驟嗎?
我們在controller根本還沒有給他方法,那它缺什麼,我們就給他什麼吧,回到 candidates_controller.rb

接下來應該大功告成了吧~至少應該可以按下提交後不會跳出錯誤了(灑花

BUT!!!!

人生就是有無限的BUT….

Rails 的預設防護

照上次的經驗來看,應該是只要新增方法就至少不會跳出錯誤了,但這裡為何會出錯呢?

原來是在rails裡面為了保護資料庫的安全,有個預設防護的機至給擋下來了。

什麼是Token?

生活情境:

小菜走在士林夜市,買了一杯位在"門口"的60嵐的珍奶並拿了20號號碼牌,想說要等,就先進去走走晃晃,看到前方剛好有一家位在"轉角"的60嵐!!!天真的小菜異想天開,想說反正都是60嵐直接拿號碼牌去領也可以吧!? 好懶得走回去喔…

跳到20號了!!!

店員: 呃(os:又來個知日 阝章)….你這個號碼牌不是我們店的喔,所以不能給你珍奶。

小菜難過的走在路上….就被端走了

token有點像在做這件事,你手上的號碼牌顯示的店面,必須要跟領取的店面相同,才能夠拿到珍奶(我們要的結果)。


<h1>新增候選人</h1>

<form action="/candidates" method="post">
  <input type="text" name="username">
  <input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
  <input type="submit">
</form>

<%= link_to '回首頁', candidates_path, class: 'btn btn-primary btn-lg'%>

我們在 new.html.erb 檔案新增第5行,到開發者工具來檢視一下token:

紅色框線value的部分就是token的值,且每次重新整理都會更新一次。

這樣一來,按下提交後就不會噴錯誤訊息囉,可是不覺得每次要做個送出資料的欄位就要打這一串很麻煩嗎?

可以用上次介紹的 form_for 就可以省略複雜的那一行了,且rails會根據你在views的哪個檔案,自動幫你在button( <%= form.submit %>)中加入文字。(ex: Updated Candidate, Edit Candidate…)

<%= form_for(物件) do |form| %>

   資料打在這裡
   
<% end %>

在Model建立表單

Rails有個方式可以使 form_for 表單綁在Model上,在終端機輸入:

rails generate model Candidate name:string party:string age:integer policy:text

or 簡單一點的寫法 (預設就是string)

rails g model Candidate name party age:integer policy:text

新增了兩個檔案:

models下的candidates.rb 及 db>migrate>*********_create_candidates.rb

其中在*********_create_candidates.rb會建立name(型態:string)、party(型態:string)、age(型態:integer)、policy(型態:text),後面的timestamp(created_at, updated_at)其實是內建的,用來紀錄提交時的時間點。

(備註: Rails在資料庫建立資料表的時候,會帶一個預設的隱藏ID的欄位,且在上面不會顯示,如果不想要ID欄位的話可以再下指令去除。)

到了這個步驟,其實並不是真正的建立表單了,我們只是告訴它,我有這個要建立表單的"計畫",所以還需要在終端機輸入:

rails db:migrate

才是告知"請按照這個計畫執行"。

如果此時再 rails db:migrate一次,並不會發生任何事,因為已經建立好了就辦法再更改,如果要更改已建立的檔案只有兩種方式:

建立一個新的表單並"存檔"後,再rails db:migrate一次。
rails db:rollback, 但請盡量少用,除非非常確定,否則用了rollback後沒辦法再回復。

之後再開schema.rb就可以確認是否是你要的結果了。

完成表單

接著在 new.html.erb 透過 form_for將表單完成後如下:

<h1>新增候選人</h1>

<%= form_for(Candidate.new) do |form| %>
  <div class="box">
    <div class="candidate_field">
      <%= form.label :name, '姓名' %>
      <%= form.text_field :name %>
    </div>
    
    <div class="candidate_field">
      <%= form.label :party, '黨派' %>
      <%= form.text_field :party %>
    </div>

    <div class="candidate_field">
      <%= form.label :age, '年紀' %>
      <%= form.text_field :age %>
    </div>

    <div class="candidate_field">
      <%= form.label :policy, '政策' %>
      <%= form.text_area :policy %>
    </div>

    <%= form.submit %>
  </div>
<% end %>

<%= link_to '回首頁', candidates_path, class: 'btn btn-primary btn-lg'%>

其中的 Candidate.new 是從Models裡的 candidate.rb 來的,但html.erb檔盡量用來做"印出內容"的事情就好,故我們不會希望在這裡用Candidate.new 來產生方法。

candidates_controller.rb 裡的new method建立一個實體變數 @candidate ,接著寫入至 new.html.erb 裡面取代原來的Candidate.new

新增候選人資料

此時按下create並不會產生任何事情,因為在controller裡的create method沒有寫任何東西,寫入下列程式至create:

class CandidatesController < ApplicationController
  def index
  end
  def new
    @candidate = Candidate.new
  end  
  def create
    render html: params
  end  
end

來看看 html: params 印出的內容

{"utf8"=>"✓", "authenticity_token"=>"0OHbZ7WkDs8NsrNHK2s7Dd9BxkITn/qegeFtUcMmE5ANKKiaxmRCOiPuChEAoKh4ho/taDYBB7GvIhTG0a1IHw==", "candidate"=>{"name"=>"Louis", "party"=>"none", "age"=>"20", "policy"=>"Rails is Good"}, "commit"=>"Create Candidate", "controller"=>"candidates", "action"=>"create"}

剛剛輸入的資料就這樣全部已hash的方式印出來了,假設我們今天要取”name”裡面的value,我們可以這樣做:

render html:params["candidate"] 
印出 {"name"=>"Louis", "party"=>"", "age"=>"", "policy"=>""}

render html:params["candidate"]["name"]
印出 Louis

寫入資料庫

create 的方法中可以用@candidate.save 來儲存使用者輸入的資料。


class CandidatesController < ApplicationController
  def index
  end
  def new
    @candidate = Candidate.new
  end  
  def create
    #params["candidate"]印出的是{"name"=>"Louis", "party"=>"", "age"=>"", "policy"=>""}
    @candidate = Candidate.new(params["candidate"])
    if @candidate.save
      render html: 'ok'
    else
      #render html: 'error'
    end
  end  
end

如果儲存成功,印出ok,因為還沒寫入false的條件,故先暫時不管。

輸入資料按下create之後,就出現錯誤了…..

Rails的另一個預設防護: Strong Parameters

為了避免有心人士在填資料時,偷塞參數或惡搞資料庫,造成安全問題,所以在使用者輸入資料要進資料庫的同時,利用permit來過濾並設定那些資料是可以進資料庫哪些不行。

class CandidatesController < ApplicationController
  def index
  end
  def new
    @candidate = Candidate.new
  end  
  def create
    clean_params = params.require(:candidate).permit(:name, :age, :policy, :party)
    @candidate = Candidate.new(clean_params)
  
    if @candidate.save
      render html: 'ok'
    else
      render html: 'error'
    end
  end  
end
clean_params = params.require(:candidate).permit(:name, :age, :policy, :party)

params.require(:candidate) => 就是找到"candidate"=>{"name"=>"Louis", "party"=>"none", "age"=>"20", "policy"=>"Rails is Good"}, "commit"=>"Create Candidate", "controller"=>"candidates", "action"=>"create"}

.permit(:name, :age, :policy, :party) => 允許:name, :age, :policy, :party 輸入的參數可以進入資料庫

就可以正常執行了。(印出ok)


礙於篇幅....好像只能先講到這,我們明天再繼續了。

參考資料:

為你自己學Ruby on Rails

“Flaming enthusiasm, backed up by horse sense and persistence, is the quality that most frequently makes for success.”

— Dale Carnegie, Motivational Expert

本文同步發佈於: https://louiswuyj.tw/


上一篇
Day20: 簡易Rails實作(上)
下一篇
Day22: 簡易Rails實作(下)
系列文
非本科之30天Ruby / Rails學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言